現在我們將 UI 加到我們的主頁面上,為了方便修改,我們將 player
場景的人物和移動輔助顯示分成兩個場景。因為移動顯示本來就是獨立的場景我們將 player
場景中的 CharacterBody2D
設為根節點然後將原本的 Node
節點和 instantiate 的 Movement
場景移除。
修改 player
腳本中計時器的初始化,因為 Timer
現在角色節點下。
invincible_timer = $"InvincibleTimer"
invincible_timer.timeout.connect(_on_invincible_timeout)
slow_timer = $"SlowTimer"
slow_timer.timeout.connect(_on_slow_timeout)
stop_timer = $"StopTimer"
stop_timer.timeout.connect(_on_stop_timeout)
組合 UI
將昨天建立的 HUD
場景加到主場景下。
# 結構如下:
|--Main
| |--Background (Scene)
| |--MoveObstacleSpwanTimer (Timer)
| |--Player (Scene)
| |--MovementUI (Scene)
| |--HUD (Scene)
修改主場景程式將 handle_game_start
綁定到昨天建立的訊號上。
var hud:CanvasLayer
func _ready():
# ...
hud = $HUD
hud.game_start.connect(handle_game_start)
接著將所有需要遊戲開始運作的物件都實作在 handle_game_start
方法中。
0. 宣告並初始化角色和背景到主場景根節點
var background
var player
func _ready():
# ...
background = $Background
player = $Player
func handle_game_start():
# 等待開始訊息顯示完畢
await get_tree().create_timer(1.6).timeout
background.start()
player.start()
await get_tree().create_timer(4).timeout
move_obstacles_timer.start()
func handle_game_end():
move_obstacles_timer.stop()
background.end()
player.end()
# 顯示結束畫面
hud.show_game_over()
background
場景:# 宣告我們的狀態並初始化為結束
enum STATE {START, STOP, END}
var State = STATE.END
# 開始才執行邏輯
func _process(delta):
match State:
STATE.START:
scroll_background(background1)
scroll_background(background2)
# 更新狀態
func start():
State = STATE.START
func stop():
State = STATE.STOP
# 結束時回復狀態
func end():
State = STATE.END
# reset to beginning state
background1.set_position(Vector2(0, 0))
background2.set_position(Vector2(0, -viewpoint_height
for bg in backgrounds_to_obstacles.keys():
for obstacle in backgrounds_to_obstacles[bg]:
obstacle.position = Vector2(0, -viewpoint_height*2)
# 宣告我們的狀態並初始化為結束
enum GAME_STATE {START, STOP, END}
var game_state = GAME_STATE.END
# 開始才執行邏輯
func _process(delta):
match game_state:
GAME_STATE.START:
# 原本的邏輯...
func _input(event):
match game_state:
GAME_STATE.START:
# 原本的邏輯...
# 更新狀態
func start():
game_state = GAME_STATE.START
# 開啟碰撞
get_node("CollisionShape2D").disabled = false
func stop():
game_state = GAME_STATE.STOP
func end():
game_state = GAME_STATE.END
player.position = start_position
# 關閉碰撞
get_node("CollisionShape2D").disabled = true
# 重置所有狀態
direction = Vector2.ZERO
dragged = false
player_anime.stop()
movement_ui.visible = false
設定我們的遊戲終止方式:碰撞到 move_obstacle
時結束遊戲。
Collision action
,設置成 END
。END
事件。signal game_end
func handle_end_state():
set_shader_para(0)
game_end.emit()
handle_game_end
到 game_end
訊號上。func _ready():
# ...
player.game_end.connect(handle_game_end)
func _ready():
# ...
body_entered.connect(_on_player_enter)
完整檔案(因為改的檔案有點多今天先貼 player 的程式)
extends CharacterBody2D
# shader
var anime_sprite = AnimatedSprite2D
# CharacterBody2D
var direction:Vector2
var dragged:bool = false
var oriPos: Vector2
var player: CharacterBody2D
var player_anime: AnimatedSprite2D
# MovementUI
var movement_ui: Node2D
# Collision Handling
@export var slow_speed:float = 1
@export var base_speed:float = 5
# start position
@export var start_position :Vector2 = Vector2(360, 1300)
var speed:float = base_speed
var invincible_timer:Timer
var slow_timer:Timer
var stop_timer:Timer
const DEFAULT = "DEFAULT"
var state:String = DEFAULT
# handle game state
enum GAME_STATE {START, STOP, END}
var game_state = GAME_STATE.END
# signal end state
signal game_end
# Called when the node enters the scene tree for the first time.
func _ready():
anime_sprite = $AnimatedSprite2D
movement_ui = $"../MovementUI"
movement_ui.visible = false
player = $"."
player_anime = player.get_node("AnimatedSprite2D")
# init player postition
player.position = start_position
invincible_timer = $"InvincibleTimer"
invincible_timer.timeout.connect(_on_invincible_timeout)
slow_timer = $"SlowTimer"
slow_timer.timeout.connect(_on_slow_timeout)
stop_timer = $"StopTimer"
stop_timer.timeout.connect(_on_stop_timeout)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
match game_state:
GAME_STATE.START:
if dragged:
player.position += direction*speed
handle_player_state()
func _input(event):
match game_state:
GAME_STATE.START:
if event is InputEventScreenTouch:
if event.is_pressed():
oriPos = event.position
dragged = true
player_anime.play()
movement_ui.visible = true
movement_ui.position = event.position
elif event.is_released():
direction = Vector2.ZERO
dragged = false
player_anime.stop()
movement_ui.visible = false
if event is InputEventScreenDrag:
direction = (event.position - oriPos).normalized()
movement_ui.rotation = Vector2.UP.angle_to(direction)
if abs(direction.y) > abs(direction.x):
player_anime.animation = "default"
elif direction.x > 0:
player_anime.animation = "turn_right"
elif direction.x < 0:
player_anime.animation = "turn_left"
func handle_player_state():
match state:
DEFAULT:
pass
"INVINCIBLE":
handle_invincible_state()
"SLOW":
handle_slow_state()
"STOP":
handle_stop_state()
"END":
handle_end_state()
state = DEFAULT
func handle_invincible_state():
get_node("CollisionShape2D").disabled = true
speed = base_speed
set_shader_para(1, Vector4(0.98, 0.91, 0.66, 1), 7)
invincible_timer.start()
func _on_invincible_timeout():
set_shader_para(0)
get_node("CollisionShape2D").disabled = false
func handle_slow_state():
set_shader_para(1, Vector4(0.76, 0.96, 0.91, 1), 4)
speed = slow_speed
slow_timer.start()
func _on_slow_timeout():
if speed == slow_speed:
set_shader_para(0)
speed = base_speed
func handle_stop_state():
speed = 0
set_shader_para(1, Vector4(0.97, 0.51, 0.58, 1), 1)
stop_timer.start()
func _on_stop_timeout():
set_shader_para(0)
speed = base_speed
func handle_end_state():
set_shader_para(0)
game_end.emit()
func set_shader_para( mode: int, color: Vector4 =Vector4.ZERO, speed: float = 0):
anime_sprite.material.set_shader_parameter("shine_color", color)
anime_sprite.material.set_shader_parameter("cycle_speed", speed)
anime_sprite.material.set_shader_parameter("mode", mode)
func start():
game_state = GAME_STATE.START
get_node("CollisionShape2D").disabled = false
func stop():
game_state = GAME_STATE.STOP
func end():
game_state = GAME_STATE.END
player.position = start_position
get_node("CollisionShape2D").disabled = true
# reset
direction = Vector2.ZERO
dragged = false
player_anime.stop()
movement_ui.visible = false
:)